import base64
import io
import os.path
import random
import time

import redis
from PIL import Image
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.edge.service import Service

from RedisClient import RedisSingle
from SlideVerifyCode import SlideVerifyCode


# https://blog.51cto.com/u_16099273/6555715
from selenium_utils import SeleniumUtils


class Toutiao:
    def __init__(self):
        # 尝试传参
        s = Service("chromedriver.exe")
        # options = Options()
        # options.add_experimental_option('excludeSwitches', ['enable-automation'])
        # options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")  # 前面设置的端口号
        self.driver = webdriver.Chrome(service=s)
        self.driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": """
            Object.defineProperty(navigator, 'webdriver', {
              get: () => undefined
            })
          """
        })
        # self.driver = webdriver.Chrome(service=s, options=chrome_options)
        # driverfile_path = r'./msedgedriver.exe'
        # driver = webdriver.Edge(executable_path=driverfile_path)
        self.driver.maximize_window()  # 最大化窗口
        redis_pool = redis.ConnectionPool(host='123.56.101.136', port=8379, db=0, password='Redis~!@19881103',
                                          max_connections=50)
        self.redis_single = RedisSingle(redis_pool)

        # 4.3计算滑动距离
        self.sc = SlideVerifyCode()
        self.vcode_name = "toutiao"
        self.crack_count = 0

    def start(self, cover, title, theme, desc):
        try:
            # 进入创作者页面
            self.driver.get("https://mp.toutiao.com/profile_v4/graphic/publish?from=toutiao_pc" + str(random.random()))
            self.driver.implicitly_wait(5)
            if os.path.exists("./cookies/toutiao.pkl"):
                SeleniumUtils.load_cookie(self.driver, ".toutiao.com", "./cookies/toutiao.pkl")
                self.driver.refresh()
            # SeleniumUtils.watting_visibility(self.driver, By.TAG_NAME, "body")
            is_load = SeleniumUtils.is_loaded(self.driver)
            if not is_load:
                print("页面加载失败！")
                return "页面加载失败"

            e_login = None
            try:
                e_login = self.driver.find_element(By.XPATH,
                                                   "//*[@id='BD_Login_Form']/div/article/article/div[2]/div/ul/li[4]/span")
            except Exception as e:
                print(e)

            if e_login is not None:
                # 切换密码登录
                SeleniumUtils.move_click(self.driver, e_login)
                time.sleep(1)
                return self.do_login_bypwd(cover, title, theme, desc)
            # else:
            #     return self.do_publish(cover, title, theme, desc)
        except Exception as e:
            print(e)
            self.driver.quit()
            return e

    def do_login_bycode(self, cover, title, theme, desc):
        time.sleep(1)
        SeleniumUtils.move_click_by(self.driver, By.XPATH,
                                  "//*[@id='BD_Login_Form']/div/article/article/div[2]/div/ul/li[4]/span")
        time.sleep(1)
        self.driver.find_element(By.XPATH,
                                 value="//*[@id='BD_Login_Form']/div/article/article/div[1]/div[1]/div[2]/article/div[1]/div/input").send_keys(
            "xxxxxxx")
        # 点击发送验证码
        SeleniumUtils.move_click_by(self.driver, By.XPATH,
                                  "//*[@id='BD_Login_Form']/div/article/article/div[1]/div[1]/div[2]/article/div[2]/div/span")

        ele_iframe = SeleniumUtils.watting_visibility(self.driver, By.XPATH,
                                                    "//*[@id='captcha_container']/iframe")
        if ele_iframe is None or not ele_iframe.is_displayed():
            return "破解验证码失败"

        self.driver.switch_to.frame(ele_iframe)

        # exec_count = 0
        # while exec_count < 5:
        #     if ele_pop.is_displayed() and ele_pop.is_enabled():
        #         exec_count = 6
        #         break
        #     print("滑块验证码执行第{}次：".format(exec_count))
        #     SeleniumUtils.move_click_by(self.driver, By.XPATH,
        #                               "//*[@id='BD_Login_Form']/div/article/article/div[1]/div[1]/div[2]/article/div[2]/div/span",
        #                               5)
        #     exec_count += 1
        #     time.sleep(1)
        # if exec_count != 6:
        #     print("滑块验证码执行失败！")
        #     return "破解滑块验证码失败"

        ret=self.crack_slide_code(cover, title, theme, desc)
        if ret != 0:
            return "破解验证码失败"

        print("ele_iframe：{}".format(ele_iframe))
        time.sleep(2)
        # 切换回主界面
        # self.driver.switch_to.default_content()

        ele_vcode = self.driver.find_element(By.XPATH,
                                             "//*[@id='BD_Login_Form']/div/article/article/div[1]/div[1]/div[2]/article/div[2]/div/span")
        # 等待破解完成
        if ele_vcode.text == "获取验证码":
            print("破解验证码失败！")
            return "破解验证码失败"

        # # 点击发送验证码
        # SeleniumUtils.move_click_by(self.driver, By.XPATH,
        #                           "//*[@id='BD_Login_Form']/div/article/article/div[1]/div[1]/div[2]/article/div[2]/div/span")

        ele_vcode = self.driver.find_element(By.XPATH,
                                             "//*[@id='BD_Login_Form']/div/article/article/div[1]/div[1]/div[2]/article/div[2]/div/div/input")

        exec_count = 0
        while exec_count < 5 * 60:
            vcode = self.redis_single.get(self.vcode_name)
            if vcode is not None and len(vcode) != 0:
                exec_count = 301
                ele_vcode.send_keys(vcode)
                break
            exec_count += 1
            time.sleep(1)
        # 5分钟内输入验证码
        # if (time.time() - last_time) / 1000 >= 60 * 5:
        if exec_count != 301:
            print("获取验证码失败！")
            return "获取验证码失败"
        # 点击登录
        SeleniumUtils.move_click_by(self.driver, By.XPATH,
                                  "//*[@id='BD_Login_Form']/div/article/article/div[1]/div[1]/div[2]/article/div[5]/button")
        return self.go_publish(cover, title, theme, desc)

    def crack_slide_code(self, cover, title, theme, desc):
        try:
            print("开始第{}次验证码破解".format(self.crack_count))
            time.sleep(2)
            # 4.2 获取滑动相关的元素
            # 选择拖动滑块的节点
            slide_element = self.driver.find_element(By.XPATH,
                                                     '//*[@id="vc_captcha_box"]/div/div/div[4]/div/div[2]/div[2]/div')
            # 获取滑块图片的节点
            slideBlock_ele = self.driver.find_element(By.XPATH,
                                                      '//*[@id="captcha-verify_img_slide"]')

            # 获取缺口背景图片节点
            slideBg = self.driver.find_element(By.XPATH,
                                               '//*[@id="vc_captcha_box"]/div/div/div[3]')

            # 下面的js代码根据canvas文档说明而来
            # JS = 'return document.getElementById("captcha-verify-image").toDataURL("image/png");'
            # # 执行 JS 代码并拿到图片 base64 数据
            # im_info = self.driver.execute_script(JS)  # 执行js文件得到带图片信息的图片数据
            # im_base64 = im_info.split(',')[1]  # 拿到base64编码的图片信息
            # im_bytes = base64.b64decode(im_base64)  # 转为bytes类型
            # with open('./images/toutiao.png', 'wb') as f:  # 保存图片到本地
            #     f.write(im_bytes)
            canvas = self.driver.find_element(By.XPATH, "//*[@id='captcha_verify_image']")
            canvas_data = self.driver.execute_script('return arguments[0].toDataURL("image/png").substring(21);',
                                                     canvas)

            # 将画面数据转换为图片
            image_data = base64.b64decode(canvas_data)
            image = Image.open(io.BytesIO(image_data))

            # 指定图片大小
            new_image = image.resize((340, 212))  # 替换为你想要的图片大小

            # 保存图片
            new_image.save('./images/toutiao.png')

            distance = self.sc.get_image_slide_distance(slideBlock_ele, slideBg, './images/toutiao.png')
            if distance == -1 and self.crack_count < 2:  # 再一次验证
                self.crack_count += 1
                return self.crack_slide_code(cover, title, theme, desc)
            # 滑动距离误差校正，滑动距离*图片在网页上显示的缩放比-滑块相对的初始位置
            print("校正后的滑动距离", distance)
            # 4.4、进行滑动
            ret = self.sc.slide_verification(self.driver, slide_element, distance - 5)
            # if ret == -1 and self.crack_count < 2:  # 在一次验证
            #     self.crack_count += 1
            #     return self.crack_slide_code(cover, title, theme, desc)
            return 0
        except Exception:
            if self.crack_count < 2:  # 在一次验证
                self.crack_count += 1
                return self.crack_slide_code(cover, title, theme, desc)
        return -1

    def do_login_bypwd(self, cover, title, theme, desc):
        self.driver.find_element(By.XPATH,
                                 value="//*[@id='BD_Login_Form']/div/article/article/div[1]/div[1]/div[2]/article/div[1]/div/input").send_keys(
            "13612935690")
        self.driver.find_element(By.XPATH,
                                 "//*[@id='BD_Login_Form']/div/article/article/div[1]/div[1]/div[2]/article/div[2]/div/div/input").send_keys(
            "Tt@19881103")

        time.sleep(2)
        # 勾选协议
        SeleniumUtils.move_click_by(self.driver, By.XPATH,
                                  "//*[@id='BD_Login_Form']/div/article/article/div[1]/div[1]/div[2]/article/div[4]/span[1]",
                                  1)
        # 点击登录
        SeleniumUtils.move_click_by(self.driver, By.XPATH,
                                  "//*[@id='BD_Login_Form']/div/article/article/div[1]/div[1]/div[2]/article/div[5]/button")
        time.sleep(2)
        # 为保证账号安全，请使用手机验证码登录
        ele_warnings = SeleniumUtils.watting_visibility(self.driver, By.XPATH,
                                                      "//*[@id='BD_Login_Form']/div/article/article/div[1]/div[1]/div[2]/article/div[3]",
                                                      5)
        if ele_warnings.text == "为保证账号安全，请使用手机验证码登录":
            return self.do_login_bycode(cover, title, theme, desc)

        return self.go_publish(cover, title, theme, desc)

    def go_publish(self, cover, title, theme, desc):
        # 等待登录完成
        ret = SeleniumUtils.watting_visibility(self.driver, By.XPATH,
                                             "//*[@id='masterRoot']/div/div[3]/section/main/div[1]/div/span/span", 30)
        if ret is None:
            print("登录失败！")
            return "登录失败"

        # cookie.pkl文件
        SeleniumUtils.save_cookie(self.driver, "./cookies/toutiao.pkl")
        return self.do_publish(cover, title, theme, desc)

    def do_publish(self, cover, title, theme, desc):
        ele_title = SeleniumUtils.watting_visibility(self.driver, By.XPATH,
                                                   "//*[@id='root']/div/div/div[1]/div[3]/div/div/div[2]/div/div/div/textarea")
        # 填充标题
        ele_title.send_keys(title)
        time.sleep(1)
        # 填充描述
        ele_desc = self.driver.find_element(By.XPATH,
                                            '//*[@id="root"]/div/div/div[1]/div[4]/div/div[1]/p[1]')
        ele_desc.send_keys(desc)

        # SeleniumUtils.move_click(self.driver, ele_desc)
        SeleniumUtils.scroll_bottom(self.driver)

        # 多标题
        SeleniumUtils.move_click_by(self.driver, By.XPATH,
                                  "//*[@id='root']/div/div/div[2]/div[2]/div[1]/div[2]/div[1]/label[2]/span/div")
        time.sleep(1)

        self.driver.find_element(By.XPATH,
                                 '//*[@id="titles.0"]/div/div/div/span/span/input').send_keys(
            theme)
        time.sleep(1)

        # 切换无封面
        SeleniumUtils.move_click_by(self.driver, By.XPATH,
                                  "//*[@id='root']/div/div/div[2]/div[2]/div[2]/div/div[2]/div/div[1]/label[3]/span/div")
        time.sleep(1)
        # 填充封面
        # if cover is not None and os.path.exists(cover):
        #     self.driver.find_element(By.XPATH,
        #                              '//*[@id="root"]/div/main/div/div[2]/div[2]/div[2]/div[3]/div[2]/div/div/label/input[@type="file"]').send_keys(
        #         cover)
        #     time.sleep(2)

        # 原创勾选
        SeleniumUtils.move_click_by(self.driver, By.XPATH, "//*[@id='originalBtn']/span/label/span/div")
        time.sleep(1)

        # 预览发布文章
        SeleniumUtils.move_click_by(self.driver, By.XPATH,
                                  '//*[@id="root"]/div/div/div[3]/div/button[3]')

        # 弹出未满足300字条件框 /html/body/div[21]/div[2]/div
        ele_alert = SeleniumUtils.watting_visibility(self.driver, By.XPATH,
                                                   "/html/body/div[21]/div[2]/div/div[2]")
        if ele_alert is not None:
            ele_btn = ele_alert.find_element(By.XPATH, "/html/body/div[21]/div[2]/div/div[2]/div[3]/button[1]")
            SeleniumUtils.move_click(self.driver, ele_btn)

        time.sleep(1)
        # 确认发布
        ret = SeleniumUtils.watting_visibility(self.driver, By.XPATH, "//*[@id='root']/div/div/div[3]/div/button[2]",
                                             30)
        if ret is None:
            print("发布失败")
            return "发布失败"

        SeleniumUtils.move_click(self.driver, ret)

        ret = SeleniumUtils.watting_visibility(self.driver, By.XPATH, "//*[@id='root']/div/main/div/article/header/h1",
                                             30)
        if ret is None:
            print("发布失败")
            return "发布失败"
        return ""


if __name__ == "__main__":
    toutiao = Toutiao()
    # 基本信息
    # 视频存放路径
    cover = r"C:\workspace\python\zimeiti\assets\1076.png"
    # cover = None
    title = "自动化测试开发"
    # 视频描述
    desc = "裸眼3D看蜘蛛侠 #搞笑 #电影 #视觉震撼"
    theme = "自动化测试"
    toutiao.start(cover, title, theme, desc)
    toutiao.driver.quit()
